package com.kainitram.library;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.j256.ormlite.dao.Dao;
import com.kainitram.designpattern.CommandInvoker;
import com.kainitram.library.declension.DeclensionHelpers;
import com.kainitram.morm.Model;
import com.kainitram.undo.DeleteCommand;
import com.kainitram.undo.UndoRedoInvoker;

import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.Button;
import android.widget.ListView;

/**
 * Base abstract class for list fragment
 * 
 * @author Mino
 * 
 * @param <T>
 *            model entity type
 */
public abstract class BaseListFragment<T extends Model> extends ListFragment
		implements OnClickListener {

	private BaseArrayAdapter<T> dataAdapter;
	private CommandInvoker commandInvoker;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		View view = inflater
				.inflate(R.layout.list_fragment_layout, null, false);
		initNewButton(view);

		return view;
	}

	@Override
	public void onActivityCreated(Bundle bundle) {
		super.onActivityCreated(bundle);

		onBeforeSetListAdapter();
		dataAdapter = getArrayAdapter(getData());
		setListAdapter(dataAdapter);
		
		this.getListView().setLongClickable(true);

		ListView listView = getListView();
		listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
		listView.setMultiChoiceModeListener(new TouchListeners());
	}

	@Override
	public void onResume() {
		super.onResume();
	}

	@Override
	public void onDestroyView() {
		super.onDestroy();
		commandInvoker = null;
	}

	public void onActivityResult(int requestCode, int resultCode, Intent data) {

		if (requestCode == MMConstants.NEW_ITEM_REQUEST_ID) {
			if (resultCode == android.app.Activity.RESULT_OK) {
				onNewItemResult(data);
			}
		}
	}

	public void onClick(View view) {

		int viewId = view.getId();

		if (viewId == R.id.newItemButton) {
			onNewItemShow();
		}
	}

	protected void onDeleteItems(List<T> items) {
		DeleteCommand<T> deleteCommand = new DeleteCommand<T>(this, items,
				dataAdapter);

		getCommandInvoker().invoke(deleteCommand);
	}

	protected void onEditItem(T item) {
	}

	protected void onShowDetailItem(T item) {

	}

	protected void onNewItemResult(Intent data) {
		@SuppressWarnings("unchecked")
		T item = (T) data.getExtras().getSerializable(
				MMConstants.EDIT_ITEM_RESULT_ITEM_EXTRA);

		dataAdapter.add(item);
		scrollToEnd();
	}

	protected void scrollToEnd() {
		getListView().setSelection(getListView().getCount() - 1);
	}

	protected void onNewItemShow() {
		Intent intent = new Intent(getActivity(), getNewItemActivityClass());
		intent.putExtra(MMConstants.EDIT_ITEM_ID_EXTRA,
				MMConstants.NEW_ITEM_ID_DEFAULT);

		startActivityForResult(intent, MMConstants.NEW_ITEM_REQUEST_ID);
	}

	protected CommandInvoker getCommandInvoker() {
		if (commandInvoker == null) {
			commandInvoker = new UndoRedoInvoker();
		}

		return commandInvoker;
	}

	// ------------------------------------
	// --------- abstract methods ---------
	// ------------------------------------

	public abstract Dao<T, Integer> getDao() throws SQLException;

	protected abstract Class<?> getNewItemActivityClass();

	/**
	 * Get data for list view
	 * 
	 * @return data
	 */
	protected abstract List<T> getData();

	/**
	 * Called befor set setListAdapter
	 */
	protected void onBeforeSetListAdapter() {
	}

	/**
	 * Get data adapter for list view
	 * 
	 * @param data
	 *            data for data adapter
	 * @return data adapter
	 */
	protected abstract BaseArrayAdapter<T> getArrayAdapter(List<T> data);

	/**
	 * Get information about button for new item
	 * 
	 * @return information about button for new item
	 */
	protected ButtonInfo getNewButtonInfo() {
		return null;
	}

	// ------------------------------------
	// --------- private methods ---------
	// ------------------------------------

	private void initNewButton(View view) {

		Button newButton = (Button) view.findViewById(R.id.newItemButton);
		ButtonInfo newButtonInfo = getNewButtonInfo();

		if (newButtonInfo != null) {
			newButton.setText(getString(newButtonInfo.getTextResId()));
			Drawable img = getResources().getDrawable(
					newButtonInfo.getImgResId());
			img.setBounds(0, 0, 40, 40);
			newButton.setCompoundDrawables(img, null, null, null);
			newButton.setOnClickListener(this);
		} else {
			newButton.setVisibility(View.INVISIBLE);
		}
	}

	private List<T> getCheckedItems() {
		List<T> ret = new ArrayList<T>();

		SparseBooleanArray checkedPositions = getListView()
				.getCheckedItemPositions();
		int size = checkedPositions.size();

		for (int i = 0; i < size; i++) {
			int key = checkedPositions.keyAt(i);
			ret.add(dataAdapter.getItem(key));
		}

		return ret;
	}

	// --------------- listeners ----------

	class TouchListeners implements MultiChoiceModeListener {

		public void onItemCheckedStateChanged(ActionMode mode, int position,
				long id, boolean checked) {
			int count = getListView().getCheckedItemCount();
			mode.setTitle(DeclensionHelpers.getWordInCorrectFormat(
					R.string.selectedCount, count, getActivity())
					+ ": "
					+ count);
		}

		public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
			int itemId = item.getItemId();
			if (itemId == R.id.miDeleteItem) {
				onDeleteItems(getCheckedItems());
				mode.finish();
				return true;
			}

			return false;
		}

		public boolean onCreateActionMode(ActionMode mode, Menu menu) {
			// Inflate the menu for the CAB
			MenuInflater inflater = mode.getMenuInflater();
			inflater.inflate(R.menu.item_popup_menu, menu);

			return true;
		}

		public void onDestroyActionMode(ActionMode mode) {
		}

		public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
			return false;
		}
	}
}
